import React, {RefObject} from "react";
import {VJsonComponent} from "../factory";
import {DataComponentProp, DataComponentState, VJsonDataComponentBase} from "../datasource";
import _ from "lodash";
import {Button, Form as AntdForm, Input, Tree as AntdTree} from 'antd'
import {YvanEvent} from "../types";
import {apply, YvanEventDispatch} from "..";
import {IReactionDisposer} from "mobx/lib/core/reaction";
import {autorun, toJS} from "mobx";
import * as Extend from "../extend";
import Select from "./form/select";
import {FormInstance} from "antd/lib/form/hooks/useForm";
import {RedoOutlined} from "@ant-design/icons";

const {Search} = Input;
const TreeNode = AntdTree.TreeNode

// const [expandedKeys, setExpandedKeys] = useState<string[]>(['0-0-0', '0-0-1']);
// const [checkedKeys, setCheckedKeys] = useState<string[]>(['0-0-0']);
// const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
// const [autoExpandParent, setAutoExpandParent] = useState<boolean>(true);

export interface TreeVJsonDataProp extends DataComponentProp<Tree> {
    /**
     * 是否可见
     */
    visible?: boolean

    autoExpandParent?: boolean
    blockNode?: boolean
    checkable?: boolean
    checkedKeys?: string[]
    checkStrictly?: boolean
    defaultCheckedKeys?: string[]
    defaultExpandAll?: boolean
    defaultExpandedKeys?: string[]
    defaultExpandParent?: boolean
    defaultSelectedKeys?: string[]
    disabled?: boolean
    draggable?: boolean
    expandedKeys?: string[]
    height?: number
    multiple?: boolean
    selectable?: boolean
    selectedKeys?: string[]
    showIcon?: boolean
    showLine?: boolean
    virtual?: boolean
    onCheck?: YvanEvent<Tree>
    onSelect?: YvanEvent<Tree>
    customFilter?: (sender: Select, keyword: string, option: { value: string, label: string }) => boolean
    icon?: string | JSX.Element
}

export interface TreeState extends DataComponentState {
    expandedKeys: any[],
    searchValue: string
    autoExpandParent: boolean
    treeData: any[]
    checkedKeys: any[]
    selectedKeys: any[]
    searchValueTemp: string
    newTreeData: any
    rememberNode?:any
    draggable?:boolean
}

@VJsonComponent({
    view: 'tree'
})
export default class Tree extends VJsonDataComponentBase<Tree, TreeVJsonDataProp, TreeState> {
    static defaultProps = {
        ...VJsonDataComponentBase.defaultProps,
        visible: true,
        bindSelected: '',
        bindChecked: '',
        defaultSearchValue: '',
        treeData: [],
        expandedKeys: [],
        checkedKeys: [],
        selectedKeys: [],
        searchValue: '',
        autoExpandParent: true,
        draggable:false
    }

    private originData: any[] = [];
    public selectData: any;
    valueWatcherDisposer?: IReactionDisposer
    nodeMap: any = {};

    treeRef: any = React.createRef();

    constructor(props: any) {
        super(props);

        _.defaults(
            props.vjson,
            Tree.defaultProps
        )
    }

    componentDidMount() {
        super.componentDidMount();
        const me = this

        this.valueWatcherDisposer = autorun(() => {
            let checkedValue = _.get(me.props.$context.scopeInstance, me.props.vjson.bindChecked) || []
            let selectedValue = _.get(me.props.$context.scopeInstance, me.props.vjson.bindSelected) || []
            if (Extend.isDebugMode()) {
                console.log('ValueWatch', this.toString(), '.checkedValue', '=', checkedValue)
                console.log('ValueWatch', this.toString(), '.selectedValue', '=', selectedValue)
            }
            this.setState({
                selectedKeys: selectedValue,
                checkedKeys: checkedValue,
            })
        });
    }

    componentWillUnmount() {
        super.componentWillUnmount();

        if (this.valueWatcherDisposer) {
            this.valueWatcherDisposer();
        }
    }

    onExpand(expandedKeys: any) {
        this.setState({
            expandedKeys,
            autoExpandParent: false,
        })
    }

    onCheck(checkedKeys: any) {
        if (this.props.vjson.bindChecked) {
            const scope = this.props.$context.scopeInstance
            if (this.props.vjson.checkStrictly) {
                _.set(scope, this.props.vjson.bindChecked, checkedKeys.checked)
            } else {
                _.set(scope, this.props.vjson.bindChecked, checkedKeys)
            }
        }
        YvanEventDispatch(this.props.vjson.onCheck, this, checkedKeys)
    }

    onSelect(selectedKeys: any, info: any) {
        let title;
        let selectedData;
        _.some(this.originData, item => {
            if (item.key == selectedKeys[0]) {
                title = item.title
                selectedData = item
                return true
            }
        })
        //记录双击时触发的第一次点击事件取到的值
        if(selectedKeys.length>0){
            this.setState({
                rememberNode:{
                    selectedKeys:selectedKeys[0],
                    title:title
                }
            })
        }
        this.selectData = {key: selectedKeys[0], title: title}
        //触发 bindSelected 改变
        if (this.props.vjson.bindSelected) {
            const scope = this.props.$context.scopeInstance
            _.set(scope, this.props.vjson.bindSelected, selectedKeys)
        }
        //触发 onRowSelect 事件
        YvanEventDispatch(this.props.vjson.onSelect, this, selectedKeys, selectedData, info)
    }

    onSearchChange(e: any) {
        this.setState({
            searchValueTemp: e.target.value
        })
    }

    onSearch(value: any) {
        this.selectData = undefined

        const expandedKeys: any[] = this.getExpandKeys(value, false)

        this.setState({
            selectedKeys: [],
            expandedKeys,
            searchValue: value,
            autoExpandParent: true,
        });

        this.buildTreeNodeBySearch(value)
    }

    onRefresh() {
        YvanEventDispatch(this.props.vjson.onRefresh, this)
    }

    buildTreeNodeBySearch(value) {
        const {treeData} = this.state;
        let newTreeData = []
        if (treeData && treeData.length > 0) {
            newTreeData = this.changeToTree(treeData, !value ? "" : value)
        }
        this.setState({
            newTreeData: newTreeData
        }, () => {
            const selected = _.get(this.props.$context.scopeInstance, this.props.vjson.bindSelected)
            if (selected && _.size(selected) > 0) {
                this.treeRef.current.scrollTo({key: selected[0]})
            }
        });
    }

    getExpandKeys(value: any, isEqual: boolean) {
        let expandedKeys: any[] = []
        if (value) {
            if (isEqual) {
                expandedKeys = this.originData
                    .map(item => {
                        if (item.key == value) {
                            return this.getParentKey(item.key, this.state.treeData);
                        }
                        return null;
                    }).filter((item: any, i: any, self: any) => item && self.indexOf(item) === i);
            } else {
                expandedKeys = this.originData
                    .map(item => {
                        if (item.title.trim().indexOf(value.trim()) > -1) {
                            return this.getParentKey(item.key, this.state.treeData);
                        }
                        return null;
                    })
                    .filter((item: any, i: any, self: any) => item && self.indexOf(item) === i);
            }
        }
        return expandedKeys
    }

    getParentKey(key: any, tree: any): any {
        let parentKey;
        for (let i = 0; i < tree.length; i++) {
            const node = tree[i];
            if (node.children) {
                if (node.children.some((item: any) => item.key === key)) {
                    parentKey = node.key;
                } else if (this.getParentKey(key, node.children)) {
                    parentKey = this.getParentKey(key, node.children);
                }
            }
        }
        return parentKey;
    }

    initShow() {
        const {treeData} = this.state;
        if (treeData && treeData.length > 0) {
            const selected = _.get(this.props.$context.scopeInstance, this.props.vjson.bindSelected)
            let defaultSearchValue = _.get(this.props.$context.scopeInstance, this.props.vjson.defaultSearchValue) || ""
            if (selected && _.size(selected) > 0) {
                const expandedKeys = this.getExpandKeys(selected[0], true)
                this.onSelect(selected, null)
                this.setState({
                    expandedKeys: expandedKeys,
                    autoExpandParent: true
                }, () => {
                    this.buildTreeNodeBySearch("")
                })
            } else if (defaultSearchValue) {
                this.setState({
                    searchValueTemp: defaultSearchValue
                }, () => {
                    this.onSearch(defaultSearchValue)
                })
            } else {
                this.onSearch("")
            }

            if (this.props.vjson.defaultExpandAll) {
                const expandedKeys: any = []
                _.forEach(this.originData, (item) => {
                    expandedKeys.push(item.key)
                })
                this.setState({
                    expandedKeys: expandedKeys,
                    autoExpandParent: true
                })
            }
        }
    }

    set data(value: any[]) {
        if (this.props.vjson.displayField && this.props.vjson.valueField && this.props.vjson.parentField) {
            const idField = this.props.vjson.valueField
            const textField = this.props.vjson.displayField
            const parentField = this.props.vjson.parentField
            const data = _.cloneDeep(value);
            // 第一遍扫描, 建立映射关系
            const nodeMap: any = {}
            this.nodeMap = nodeMap
            const rootNode: any[] = []
            const originData: any[] = []
            for (let i = 0; i < data.length; i++) {
                const row = data[i]
                const item = {
                    title: row[textField],
                    key: row[idField],
                    row: row
                }
                nodeMap[row[idField]] = item
                originData.push(item)
            }

            this.originData = originData

            // 第二遍扫描，建立父子关系
            for (let i = 0; i < data.length; i++) {
                const row = data[i]
                const parent = row[parentField]
                const id = row[idField]

                if (!parent || parent === '0') {
                    // 没有父亲，作为根节点
                    rootNode.push(nodeMap[id])
                } else if (nodeMap.hasOwnProperty(parent)) {
                    //找到父亲
                    const parentNode = nodeMap[parent]
                    if (parentNode.hasOwnProperty('children')) {
                        parentNode.children.push(nodeMap[id])
                    } else {
                        parentNode.children = [nodeMap[id]]
                    }
                } else {
                    // 没有找到父亲，作为根节点
                    rootNode.push(nodeMap[id])
                }
            }

            this.setState({
                treeData: rootNode
            }, () => {
                //刘壮添加，回显数据并展开
                this.initShow()
            })
        } else {
            this.originData = value
            this.setState({
                treeData: value
            }, () => {
                this.initShow()
            })
        }
    }

    get data(): any[] {
        return this.state.treeData
    }

    getSubItems(id: any): any[] {
        return this.nodeMap[id]
    }

    matcher(title: any, keywords: any, key: any) {

        if (typeof this.props.vjson.customFilter === 'function') {
            return YvanEventDispatch(this.props.vjson.customFilter, this, keywords,
                {
                    value: key,
                    label: title
                }
            )
        }

        return title.trim().indexOf(keywords.trim()) > -1
    }

    filterNode(children: any, keywords: any) {
        return children
            && children.length
            //@ts-ignore
            && !!children.find(({children, title, key}) =>
                this.matcher(title, keywords, key) ||
                this.filterNode(children, keywords))
    }

    changeToTree(data: any, keywords: any, isSubNode?: boolean) {
        keywords = keywords ? keywords.trim() : ""
        //@ts-ignore
        return data.map(({children, key, title}) => {
            const index = title.indexOf(keywords);
            const beforeStr = title.substr(0, index);
            const afterStr = title.substr(index + keywords?.length);
            const newTitle = index > -1 ? (
                <span>
                                {beforeStr}
                    <span style={{color: "#f50"}}>{keywords}</span>
                    {afterStr}
                            </span>
            ) : (
                <span>{title}</span>
            )

            if (index > -1 || isSubNode) {
                if (children) {
                    return (
                        <TreeNode key={key} title={newTitle}>
                            {this.changeToTree(children, keywords, true)}
                        </TreeNode>
                    )
                }
                else {
                    return (
                        <TreeNode
                            key={key}
                            title={newTitle}/>
                    )
                }
            }

            if (children) {
                let isShow = this.filterNode(children, keywords)
                if (isShow) {
                    return (
                        <TreeNode className={isShow ? '' : 'node-hidden'} key={key} title={newTitle}>
                            {this.changeToTree(children, keywords)}
                        </TreeNode>
                    )
                } else {
                    isShow = this.matcher(title, keywords, key)
                    if (isShow) {
                        return (
                            <TreeNode className={isShow ? '' : 'node-hidden'} key={key} title={newTitle}>
                            </TreeNode>
                        )
                    }
                }
            } else {
                const isShow = this.matcher(title, keywords, key)
                if (isShow) {
                    return (
                        <TreeNode
                            className={isShow ? '' : 'node-hidden'}
                            key={key}
                            title={newTitle}/>
                    )
                }
            }
        })
    }

    onDoubleClick(e:any){
        //触发 onDoubleClick 事件
        YvanEventDispatch(this.props.vjson.onDoubleClick, this, this.state.rememberNode.selectedKeys, this.state.rememberNode.title)
    }

    onDrop(info:any){//拖动事件
        const dropKey = info.node.key;//拖动的节点最终位置后面的节点key
        const dragKey = info.dragNode.key;//当前拖动的节点key
        const dropPos = info.node.pos.split('-');
        const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);//向下拖动为1，向上拖动为-1
        let data = [...this.state.newTreeData];
        const loop = (data, key, callback) => {
            for (let i = 0; i < data.length; i++) {
                if (data[i].key === key) {
                    return callback(data[i], i, data);
                }
                if (data[i].props.children) {
                    loop(data[i].props.children, key, callback);
                }
            }
        };
        let dragObj;
        loop(data, dragKey, (item, index, arr) => {
            arr.splice(index, 1);
            dragObj = item;
        });
        if (!info.dropToGap) {//如果拖动到内部
            let newData=$.extend(true, [], data);
            loop(newData, dropKey, (item) => {
                item.props.children= item.props.children || [];
                item.props.children.unshift(dragObj);
            });
            this.setState({
                newTreeData:newData
            });
        }else if ((info.node.props.children || []).length > 0 && info.node.props.expanded && dropPosition === 1 ) {
            let newData=$.extend(true, [], data);
            loop(newData, dropKey, (item) => {
                item.props.children= item.props.children || [];
                item.props.children.unshift(dragObj);
            });
            this.setState({
                newTreeData:newData
            });
        }else{
            let ar;
            let i;
            loop(data, dropKey, (item, index, arr) => {
                ar = arr;
                i = index;
            });
            if (dropPosition === -1) {//向上拖动
                ar.splice(i, 0, dragObj);
            } else {//向下拖动
                ar.splice(i + 1, 0, dragObj);
            }
            this.setState({
                newTreeData:data
            });
        }
    }

    render() {
        if (!this.props.vjson.visible) {
            return null
        }

        const {newTreeData} = this.state;
        if (!newTreeData || newTreeData.length == 0) {
            return null
        }
        return (
            <div>
                <div style={{display:"flex", flexDirection: "row"}}>
                    {
                        this.props.vjson.searchable &&
                        <Search style={{marginBottom: 8}} placeholder="请输入" value={this.state.searchValueTemp}
                                onChange={this.onSearchChange.bind(this)}
                                onSearch={this.onSearch.bind(this)}/>
                    }
                    {
                        this.props.vjson.refreshable &&
                        <Button icon={<RedoOutlined />}
                            onClick={this.onRefresh.bind(this)}>
                        </Button>
                    }
                </div>
                <AntdTree
                    ref={this.treeRef}
                    checkable={apply(this, this.props.vjson.checkable)}
                    checkStrictly={apply(this, this.props.vjson.checkStrictly)}
                    defaultExpandAll={apply(this, this.props.vjson.defaultExpandAll)}
                    expandedKeys={toJS(this.state.expandedKeys)}
                    autoExpandParent={toJS(this.state.autoExpandParent)}
                    onExpand={this.onExpand.bind(this)}
                    onCheck={this.onCheck.bind(this)}
                    checkedKeys={toJS(this.state.checkedKeys)}
                    onSelect={this.onSelect.bind(this)}
                    selectedKeys={toJS(this.state.selectedKeys)}
                    height={apply(this, this.props.vjson.height)}
                    onDoubleClick={this.onDoubleClick.bind(this)}
                    draggable={this.props.vjson.draggable}
                    blockNode
                    onDrop={this.onDrop.bind(this)}
                >
                    {newTreeData}
                </AntdTree>
            </div>
        )
    }
}